﻿Imports System.Collections.Generic
Imports System.Text
Imports System.Windows.Forms

Namespace TL.Console
    ''' <summary>
    ''' Darstellung einer 'virtuellen' Konsole, die man in eine Windowsanwendung integrieren kann.
    ''' 
    ''' VirtualConsole - © Konstantin Gross
    ''' </summary>
    Public Class VirtualConsole
        Protected Delegate Sub EditLabelDelegate(ByVal lbl As Label, ByVal str As String)

        ' Ausgeführte Kommandos
        Protected cmd_history_list As String()
        Protected cmd_history_pointer As Integer
        Protected cmd_scroll_index As Integer

        ' Konsolen Log Bildschirm
        Protected con_log_list As String()
        Protected con_log_pointer As Integer
        Protected scroll_index As Integer

        ' Sonstiges
        Protected displayed_line As Integer
        Protected parent_form As Form

#Region "Form Controls"

        Private ConsolePanel As System.Windows.Forms.Panel
        Private ConsoleLabel As System.Windows.Forms.Label
        Private ConsoleCommandTextBox As System.Windows.Forms.TextBox
        Private ConsoleTextBox As System.Windows.Forms.Label()

        Private Sub InitializeConsoleComponent()
            ConsolePanel = New System.Windows.Forms.Panel()
            ConsoleTextBox = New System.Windows.Forms.Label(displayed_line - 1) {}
            ConsoleCommandTextBox = New System.Windows.Forms.TextBox()
            ConsoleLabel = New System.Windows.Forms.Label()
            ConsolePanel.SuspendLayout()

            ' 
            ' ConsolePanel
            ' 
            Me.ConsolePanel.BackColor = System.Drawing.Color.Black
            Me.ConsolePanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle

            For i As Integer = 0 To ConsoleTextBox.Length - 1
                Me.ConsoleTextBox(i) = New Label()
                Me.ConsolePanel.Controls.Add(ConsoleTextBox(i))
            Next

            Me.ConsolePanel.Controls.Add(Me.ConsoleCommandTextBox)
            Me.ConsolePanel.Controls.Add(Me.ConsoleLabel)
            Me.ConsolePanel.Dock = System.Windows.Forms.DockStyle.Top
            Me.ConsolePanel.Location = New System.Drawing.Point(0, 410)
            Me.ConsolePanel.Name = "ConsolePanel"
            Me.ConsolePanel.Size = New System.Drawing.Size(818, 35 + displayed_line * 18)
            Me.ConsolePanel.TabIndex = 0
            Me.ConsolePanel.Visible = False
            ' 
            ' ConsoleTextBox's
            ' 
            For i As Integer = 0 To ConsoleTextBox.Length - 1
                Me.ConsoleTextBox(i).BackColor = System.Drawing.Color.Black
                Me.ConsoleTextBox(i).Dock = System.Windows.Forms.DockStyle.Top
                Me.ConsoleTextBox(i).Font = New System.Drawing.Font("Consolas", 8, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CByte(0))
                Me.ConsoleTextBox(i).ForeColor = System.Drawing.Color.Orange
                Me.ConsoleTextBox(i).Location = New System.Drawing.Point(0, i * 18)
                Me.ConsoleTextBox(i).Name = "ConsoleLabel" & i
                Me.ConsoleTextBox(i).Size = New System.Drawing.Size(816, 18)
                Me.ConsoleTextBox(i).Text = "" & i
                AddHandler Me.ConsoleTextBox(i).Click, New System.EventHandler(AddressOf Me.ConsoleLabel_Click)
            Next

            ' 
            ' ConsoleCommandTextBox
            ' 
            Me.ConsoleCommandTextBox.BackColor = System.Drawing.Color.Black
            Me.ConsoleCommandTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None
            Me.ConsoleCommandTextBox.Dock = System.Windows.Forms.DockStyle.Bottom
            Me.ConsoleCommandTextBox.Font = New System.Drawing.Font("Consolas", 8, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CByte(0))
            Me.ConsoleCommandTextBox.ForeColor = System.Drawing.Color.Orange
            Me.ConsoleCommandTextBox.Location = New System.Drawing.Point(0, 89)
            Me.ConsoleCommandTextBox.Name = "ConsoleCommandTextBox"
            Me.ConsoleCommandTextBox.Size = New System.Drawing.Size(816, 15)
            Me.ConsoleCommandTextBox.TabIndex = 2
            AddHandler Me.ConsoleCommandTextBox.KeyDown, New System.Windows.Forms.KeyEventHandler(AddressOf Me.ConsoleCommandTextBox_KeyDown)
            ' 
            ' ConsoleLabel
            ' 
            Me.ConsoleLabel.BackColor = System.Drawing.Color.Black
            Me.ConsoleLabel.Dock = System.Windows.Forms.DockStyle.Top
            Me.ConsoleLabel.Font = New System.Drawing.Font("Consolas", 8, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CByte(0))
            Me.ConsoleLabel.ForeColor = System.Drawing.Color.White
            Me.ConsoleLabel.Location = New System.Drawing.Point(0, 0)
            Me.ConsoleLabel.Name = "ConsoleLabel"
            Me.ConsoleLabel.Size = New System.Drawing.Size(816, 18)
            Me.ConsoleLabel.TabIndex = 0
            Me.ConsoleLabel.Text = "- MultiPlug Console -"
            AddHandler Me.ConsoleLabel.Click, New System.EventHandler(AddressOf Me.ConsoleLabel_Click)

            Me.ConsolePanel.ResumeLayout(False)
            Me.ConsolePanel.PerformLayout()

            parent_form.Controls.Add(Me.ConsolePanel)
        End Sub

        Private Sub ConsoleTextBox_Click(ByVal sender As Object, ByVal e As EventArgs)
            ConsoleCommandTextBox.Focus()
        End Sub

        Private Sub ConsoleLabel_Click(ByVal sender As Object, ByVal e As EventArgs)
            ConsoleCommandTextBox.Focus()
        End Sub

        Private Sub ConsoleCommandTextBox_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
            If e.KeyValue = 13 Then
                'Wenn ENTER gedrückt wurde, führe das Kommando aus
                Dim cmd As String = ConsoleCommandTextBox.Text
                ConsoleCommandTextBox.Text = ""
                ExecuteCommand(cmd)
            ElseIf e.KeyValue = 38 Then
                ' Go down in the history list
                cmd_scroll_index -= 1
                If cmd_scroll_index < 0 Then
                    cmd_scroll_index = cmd_history_list.Length - 1
                End If


                ' Display command in testbox
                Dim projected_index As Integer = (cmd_history_list.Length + cmd_history_pointer + cmd_scroll_index) Mod (cmd_history_list.Length)
                ConsoleCommandTextBox.Text = cmd_history_list(projected_index)
            ElseIf e.KeyValue = 40 Then
                ' Go down in the history list
                cmd_scroll_index += 1
                If cmd_scroll_index > cmd_history_list.Length - 1 Then
                    cmd_scroll_index = 0
                End If

                ' Display command in testbox
                Dim projected_index As Integer = (cmd_history_list.Length + cmd_history_pointer + cmd_scroll_index) Mod (cmd_history_list.Length)
                ConsoleCommandTextBox.Text = cmd_history_list(projected_index)
            End If
        End Sub


#End Region

        ''' <summary>
        ''' In welcher Form soll die Konsole angezeigt werden
        ''' </summary>
        ''' <param name="parent">Das Parent-Form mitteilen</param>
        Public Sub New(ByVal parent As Form)
            parent_form = parent

            ' Konstruktor mit Standardwerten
            con_log_list = New [String](99) {}
            con_log_pointer = 0
            scroll_index = 0

            ' Ausgeführte Kommandos
            cmd_history_list = New [String](49) {}
            cmd_history_pointer = 0
            cmd_scroll_index = 0

            displayed_line = 6

            InitializeConsoleComponent()

            WriteLine("Geben Sie help ein, um eine Liste der Kommandos anzuzeigen")
            WriteLine("----------------------------------------------------------")
            WriteLine(" - Benutze ^ um die Konsole anzuzeigen/zu verstecken")
            WriteLine(" - Benutze Hoch/Runter um die letzten Kommandos zu sehen")
            WriteLine(" - Benutze Bildhoch/Bildrunter um in der Konsolenlog zu scrollen")
            WriteLine("- © 2007 Konstantin Gross - www.texturenland.de -")
        End Sub

        ''' <summary>
        ''' Setzt die Sichtbarkeit der Konsole.
        ''' </summary>
        ''' <param name="visible">Soll die Konsole sichtbar sein?</param>
        Public Overridable Sub SetVisibility(ByVal visible As Boolean)
            ConsolePanel.Visible = visible

            If visible = True Then
                ConsoleCommandTextBox.Focus()
            Else
                parent_form.Focus()
            End If
        End Sub

        ''' <summary>
        ''' Wird ausgeführt wenn man hochscrollt im Log Bildschirm.
        ''' </summary>
        Public Overridable Sub Scroll_Up()
            If scroll_index >= con_log_list.Length - 1 - displayed_line Then
                Return
            End If
            scroll_index += 1

            RefreshLayout()
        End Sub

        ''' <summary>
        ''' Wird ausgeführt wenn man runterscrollt im Log Bildschirm.
        ''' </summary>
        Public Overridable Sub Scroll_Down()
            If scroll_index <= 0 Then
                Return
            End If

            scroll_index -= 1

            RefreshLayout()
        End Sub

        ''' <summary>
        ''' Rufe den aktuellen Sichtbarkeitsstauts der Konsole ab.
        ''' </summary>
        ''' <returns>Ist die Konsole sichtbar?</returns>
        Public Overridable Function GetVisibility() As Boolean
            Return ConsolePanel.Visible
        End Function

        ''' <summary>
        ''' Führt die eingegebenen Kommandos aus.
        ''' </summary>
        ''' <param name="command">Kommando</param>
        Protected Overridable Sub ExecuteCommand(ByVal command As [String])
            cmd_scroll_index = 0

            ' Wenn kein Kommando angegeben wurde, egal =)
            If command = "" Then
                Return
            End If

            scroll_index = 0

            ' Speichere die Kommandis in der History
            cmd_history_list(cmd_history_pointer) = command
            cmd_history_pointer += 1

            If cmd_history_pointer > cmd_history_list.Length - 1 Then
                cmd_history_pointer = 0
            End If

            WriteLine(">" & command)
            command = command.ToLower()

            ' Führe das Kommando aus, wenn es existiert
            Select Case command
                Case "exit"
                    Application.[Exit]()
                    Exit Select

                Case "help"
                    WriteLine("Das ist die Liste der vorhandenen Kommandos:")
                    WriteLine(" - help : Dieser Bildschirm.")
                    WriteLine(" - version : Erhält die Programmversion.")
                    WriteLine(" - hide : Verstecke die Konsole.")
                    WriteLine(" - clear : Lösche den Text in der Konsole.")
                    WriteLine(" - message : Zeigt eine MessageBox an.")
                    WriteLine(" - test : Teste eine interne Funktion.")
                    WriteLine(" - exit : Beende die Anwendung.")
                    Exit Select

                Case "hide"
                    SetVisibility(False)
                    Exit Select

                Case "clear"
                    Clear()
                    Exit Select

                Case "version"
                    WriteLine(Application.ProductName & " - v" & Application.ProductVersion)
                    Exit Select

                Case "message"
                    MessageBox.Show("ICH LEBE!!!!!")
                    Exit Select

                Case "test"
                    For i As Integer = 0 To 100
                        For j As Integer = 0 To 500
                            Application.DoEvents()
                        Next

                        WriteLineForProgressive("WoW ein Fortschritt", i, 100)
                    Next
                    Exit Select
                Case Else


                    WriteLine("Unbekanntes Kommando '" & command & "'.")
                    Exit Select
            End Select
        End Sub

        ''' <summary>
        ''' Schreibe eine Text in die Konsole.
        ''' </summary>
        ''' <param name="str">Zu übergebender Text</param>
        Public Overridable Sub WriteLine(ByVal str As String)
            con_log_list(con_log_pointer) = str

            con_log_pointer += 1

            If con_log_pointer > con_log_list.Length - 1 Then
                con_log_pointer = 0
            End If

            RefreshLayout()
        End Sub

        ''' <summary>
        ''' Schreibe einen Text mit Fortschrittsanzeige
        ''' </summary>
        ''' <param name="str">Zu übergebender Text</param>
        ''' <param name="progress">Fortschritt</param>
        ''' <param name="total">Maximal</param>
        Public Overridable Sub WriteLineForProgressive(ByVal str As String, ByVal progress As Integer, ByVal total As Integer)
            ' Message in more states
            If (ReadlastLine() IsNot Nothing) AndAlso (ReadlastLine().Length >= str.Length) AndAlso ((ReadlastLine()).Substring(0, str.Length) = str) Then
                Dim prozent As Integer = (progress * 100) \ total
                WriteErasingCurrentLine(str & " [" & prozent & "%]")
            Else
                Dim prozent As Integer = (progress * 100) \ total
                WriteLine(str & " [" & prozent & "%]")
            End If
        End Sub

        ''' <summary>
        ''' Lese letzte Zeile in der Konsole.
        ''' </summary>
        ''' <returns>Letze Zeile in der Konsole</returns>
        Protected Overridable Function ReadlastLine() As [String]
            Return con_log_list((If(con_log_pointer = 0, con_log_list.Length - 1, con_log_pointer - 1)))
        End Function

        ''' <summary>
        ''' Schreibe einen Text in die Konsole.
        ''' </summary>
        ''' <param name="str">Zu übergebender Text</param>
        Public Overridable Sub Write(ByVal str As [String])
            con_log_list(con_log_pointer) = con_log_list(con_log_pointer) & str
            RefreshLayout()
        End Sub

        ''' <summary>
        ''' Lösche aktuelle Zeile
        ''' </summary>
        ''' <param name="str">Zu löschender Text</param>
        Protected Overridable Sub WriteErasingCurrentLine(ByVal str As [String])

            con_log_list((If(con_log_pointer = 0, con_log_list.Length - 1, con_log_pointer - 1))) = str
            RefreshLayout()
        End Sub

        ''' <summary>
        ''' Löscht den Text in der Konsolenanzeige.
        ''' </summary>
        Public Overridable Sub Clear()
            con_log_list = New [String](49) {}
            con_log_pointer = 0

            RefreshLayout()
        End Sub

        ''' <summary>
        ''' Aktualisiere alle Controls.
        ''' </summary>
        Protected Overridable Sub RefreshLayout()
            Dim projected_index As Integer = 0
            For line_counter As Integer = 0 To displayed_line - 1
                projected_index = ((con_log_list.Length - 1) + con_log_pointer - line_counter - scroll_index) Mod (con_log_list.Length)
                parent_form.Invoke(New EditLabelDelegate(AddressOf EditLabel), ConsoleTextBox(line_counter), con_log_list(projected_index))
            Next
        End Sub

        Protected Overridable Sub EditLabel(ByVal lbl As Label, ByVal str As [String])
            lbl.Text = str
        End Sub
    End Class
End Namespace